#ifndef _CANARY2_NET_TCP_H_
#define _CANARY2_NET_TCP_H_

#include "../sys/fd_guard.h"
#include <span>
#include <sys/socket.h>
#include <ranges>
#include <vector>
#include <filesystem>

namespace wilson::net
{
    class tcp_conn
    {
    public:
        constexpr tcp_conn() = default;
        tcp_conn(sys::fd_guard fd);
        tcp_conn(const tcp_conn&) = delete;
        tcp_conn(tcp_conn&&) = default;
        tcp_conn& operator=(tcp_conn&&) = default;
        tcp_conn& operator=(const tcp_conn&) = delete;

        void close();
        void shutdown();
        void shutdown_read();
        void shutdown_write();

        constexpr bool empty() const noexcept 
        { 
            return tc_fd.empty(); 
        }

    private:
        template<typename T>
        void set_sockopt(int level, int optname, const T& optv)
        {
            int err = ::setsockopt(
                tc_fd, level, optname, &optv, sizeof(T)
            );
            if (err == -1)
            {
                throw ::std::runtime_error{ ::strerror(errno) };
            }
        }

        friend class tcp_io;

        const sys::fd_guard& fd() const noexcept { return tc_fd; }

    private:
        sys::fd_guard tc_fd;
    };

    template<typename R>
    concept tcp_sendable_range = 
        ::std::ranges::sized_range<R> && 
        ::std::ranges::input_range<R> && 
        ::std::same_as<
            ::std::iter_value_t<::std::ranges::iterator_t<R>>, 
            ::std::byte
        >;

    class tcp_io
    {
    public:
        tcp_io(const tcp_conn& conn)
            : ti_fd{ conn.fd() }
        {
        }

        ssize_t send(sys::borrowed_fd filefd, size_t count, int flags = 0);
        ssize_t send(tcp_sendable_range auto const& r, int flags = 0);
        ssize_t send(::std::string_view str, int flags = 0);
        ssize_t send(::std::span<const ::std::byte> buffer, int flags = 0);
        ssize_t sendmsg(const ::msghdr* hdr, int flags = 0);
        ssize_t write(::std::span<const ::std::byte> buffer);
        ssize_t recv(::std::span<::std::byte> buffer, int flags = 0);
        ssize_t recvmsg(::msghdr* hdr, int flags = 0);
        ssize_t read(::std::span<::std::byte> buffer);

        sys::borrowed_fd fd() const noexcept { return ti_fd; }

    private:
        sys::borrowed_fd ti_fd;
    };

    
    ssize_t tcp_io::send(tcp_sendable_range auto const& r, int flags) 
    {
        ::std::vector<::std::byte> buffer(r.size());
        ::std::copy(r.begin(), r.end(), buffer.begin());
        return send(buffer, flags);
    }
}

#endif
